// ArchiveOpenCallback.cpp

#include "StdAfx.h"

#include "Common/String/StringConvert.h"

#include "Common/MS/PropVariant.h"

#include "Common/Stream/FileStreams.h"

#include "ArchiveOpenCallback.h"

namespace Common
{
namespace Archive
{


STDMETHODIMP COpenCallbackImp::SetTotal(const UINT64 *files, const UINT64 *bytes)
{
  try
  {
	  if (ReOpenCallback)
		return ReOpenCallback->SetTotal(files, bytes);
	  if (!Callback)
		return S_OK;
	  return Callback->Open_SetTotal(files, bytes);
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

STDMETHODIMP COpenCallbackImp::SetCompleted(const UINT64 *files, const UINT64 *bytes)
{
  try
  {
	  if (ReOpenCallback)
		return ReOpenCallback->SetCompleted(files, bytes);
	  if (!Callback)
		return S_OK;
	  return Callback->Open_SetCompleted(files, bytes);
  } catch(...) 
  { return E_OUTOFMEMORY; }
}
  
STDMETHODIMP COpenCallbackImp::GetProperty(PROPID propID, PROPVARIANT *value)
{
  try
  {
	  CComPropVariant prop;
	  if (_subArchiveMode)
		switch(propID)
		{
		  case kpidName: prop = _subArchiveName; break;
		}
	  else
		switch(propID)
		{
		  case kpidName:  prop = CComBSTR(_fileInfo.Name); break;
		  case kpidIsDir:  prop = _fileInfo.IsDir(); break;
		  case kpidSize:  prop = _fileInfo.Size; break;
		  case kpidAttrib:  prop = (UINT32)_fileInfo.Attributes; break;
		  case kpidCTime:  prop = _fileInfo.CreationTime; break;
		  case kpidATime:  prop = _fileInfo.AccessTime; break;
		  case kpidMTime:  prop = _fileInfo.ModificationTime; break;
		}
	  prop.Detach(value);
	  return S_OK;
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

int COpenCallbackImp::FindName(const CString &name)
{
  for (size_t i = 0; i < FileNames.size(); i++)
    if (name.CompareNoCase(FileNames[i]) == 0)
      return i;
  return -1;
}

struct CInFileStreamVol: public CInFileStream
{
  CString Name;
  COpenCallbackImp *OpenCallbackImp;
  CComPtr<IArchiveOpenCallback> OpenCallbackRef;
  ~CInFileStreamVol()
  {
    if (OpenCallbackRef)
    {
      int index = OpenCallbackImp->FindName(Name);
      if (index >= 0)
		  OpenCallbackImp->FileNames.erase(OpenCallbackImp->FileNames.begin() + index);
    }
  }
};

STDMETHODIMP COpenCallbackImp::GetStream(const wchar_t *name, IInStream **inStream)
{
	try
	{
	  if (_subArchiveMode)
			return S_FALSE;
	  if (Callback)
	  {
		HRESULT hRes = Callback->Open_CheckBreak();
		if(S_OK != hRes)
			return hRes;
	  }
	  *inStream = NULL;
	  CString fullPath = _folderPrefix + name;
	  if (!_fileInfo.Find(fullPath))
		return S_FALSE;
	  if (_fileInfo.IsDir())
		return S_FALSE;
	  CInFileStreamVol *inFile = new CComObject<CInFileStreamVol>;
	  inFile->FinalConstruct();
	  CComPtr<IInStream> inStreamTemp = inFile;
	  if (!inFile->Open(fullPath))
		return ::GetLastError();
	  *inStream = inStreamTemp.Detach();
	  inFile->Name = name;
	  inFile->OpenCallbackImp = this;
	  inFile->OpenCallbackRef = this;
	  FileNames.push_back(name);
	  TotalSize += _fileInfo.Size;
	  return S_OK;
  } catch(...) 
  { return E_OUTOFMEMORY; }
}

#ifndef _NO_CRYPTO
STDMETHODIMP COpenCallbackImp::CryptoGetTextPassword(BSTR *password)
{
  try
  {
	  if (ReOpenCallback)
	  {
		CComPtr<ICryptoGetTextPassword> getTextPassword;
		ReOpenCallback.QueryInterface(&getTextPassword);
		if (getTextPassword)
		  return getTextPassword->CryptoGetTextPassword(password);
	  }
	  if (!Callback)
		return E_NOTIMPL;
	  return Callback->Open_CryptoGetTextPassword(password);
  } catch(...) 
  { return E_OUTOFMEMORY; }
}
#endif
  
}
}
